/*
jQuery 甘特图
jQuery.ganttchart.js
---------------------
* 版本: v0.1
* 作者: 小浩叔叔
* 日期: 2020-12-16
* http://whitesoft.cn
* 依赖：
*     jquery.js
*
* 兼容性：
*     IE8+ & Other
*     在 jQuery v1.11.3 和 jQuery v2.1.4 环境中测试通过
*
*/

$(function () {
    $.fn.ganttchart = function (options, param) {

        if (typeof options == 'string') {  /* 触发操作方法 */
            return $.fn.ganttchart.methods[options](this, param);
        }

        var opts = $.extend({}, $.fn.ganttchart.defaults, options);

        opts.dom = this;
        opts.start = new Date();
        opts.end = new Date();
        opts.GanttGroup = { Groups: new Array(), Items: new Array() };
        opts.GanttCellWidth = 50;

        /* 初始化 */
        opts._init = function () {
            opts._read_time_area();
            opts._init_dom();

            /* 如果未设置高度，自适应高度，填充窗口。 */
            if (typeof opts.height == "undefined" || opts.height <= 0) {
                var osPos = $(opts.dom).offset();
                var wHeight = $(window).height();
                opts.height = wHeight - osPos.top - 40;
                if (opts.height < 300) {
                    opts.height = 300;
                }
            }

            /* 如果未设置数据列宽度，则自适应宽度，显示不全则显示50%，能显示全则全部显示。 */
            if (typeof opts.head_width == "undefined" || opts.head_width <= 0) {
                var width_count = 0;
                for (var i = 0; i < opts.columns.length; i++) {
                    var col = opts.columns[i];
                    if (typeof col.hidden != "undefined" && col.hidden == true) {  /* 该列需要隐藏，不再处理 */ continue; }
                    width_count += col.width;
                }
                var wWidth = $(window).width();
                if (width_count > (wWidth / 2)) {
                    opts.head_width = (wWidth / 2);
                }
            }
            
            var toolbar_height = $(".ganttchart-toolbar", opts.dom).height();

            $(".ganttchart-head", opts.dom).width(opts.head_width);  //设置数据行宽度
            $(".ganttchart-head", opts.dom).height(opts.height - toolbar_height);  //设置数据区高度
            $(".ganttchart-gantt", opts.dom).height(opts.height - toolbar_height);  //设置甘特图内容区高度

            /* 甘特图内容区 滚动条滚动时触发 */
            $(".ganttchart-gantt", opts.dom).scroll(function() {
                /* 甘特图内容区 滚动条滚动时 同时也滚动数据区的高度，并且保持标题在显示区域上面 */
                var top = $(".ganttchart-gantt", opts.dom).scrollTop();  //获取 甘特图内容区 滚动条位置
                $(".ganttchart-head", opts.dom).scrollTop(top);  // 设置数据区滚动条位置
                $(".ganttchart-gantt-title", opts.dom).css("top", top);   // 设置 甘特图内容区 标题的显示位置
            });
            
            /* 甘特图内容区 滚动条滚动时触发 */
            $(".ganttchart-head", opts.dom).scroll(function(){
                /* 数据区 滚动条滚动时 保持标题在显示区域上面 */
                var top = $(".ganttchart-head", opts.dom).scrollTop();  //获取 数据区 滚动条位置
                $(".ganttchart-head-title", opts.dom).css("top", top);  // 设置 数据区 标题的显示位置
            });

            opts._build_head_title();
            opts._build_head_content();

            opts._build_gantt();
        };/* options._init() End */
 
        opts._init_dom = function(){
            $(opts.dom).empty();

            /* 甘特图 */
            var $ganttchart = $('<div class="ganttchart"></div>');
            $(opts.dom).append($ganttchart);

            /* 工具栏区域 */
            var $toolbar = $('<div class="ganttchart-toolbar"></div>');
            $ganttchart.append($toolbar);

            /* 左侧头部数据区 */
            var $head = $('<div class="ganttchart-head"></div>');
            $ganttchart.append($head);
            
            /* 左侧头部数据区标题 */
            var $head_title = $('<div class="ganttchart-head-title"></div>');
            $head.append($head_title);

            /* 左侧头部数据区内容 */
            var $head_content = $('<div class="ganttchart-head-content"></div>');
            $head.append($head_content);
            
            /* 右侧甘特图显示区 */
            var $gantt = $('<div class="ganttchart-gantt"></div>');
            $ganttchart.append($gantt);

            /* 右侧甘特图 标题 */
            var $gantt_title = $('<div class="ganttchart-gantt-title"></div>');
            $gantt.append($gantt_title);

            /* 右侧甘特图 标题 分组（年月） */
            var $gantt_title_groups = $("<div class='ganttchart-gantt-title-groups'></div>");
            $gantt_title.append($gantt_title_groups);

            /* 右侧甘特图 标题 项目（月日） */
            var $gantt_title_items = $("<div class='ganttchart-gantt-title-items'></div>");
            $gantt_title.append($gantt_title_items);

            /* 右侧甘特图 内容区 */
            var $gantt_content = $('<div class="ganttchart-gantt-content"></div>');
            $gantt.append($gantt_content);

            /* 右侧甘特图 内容区 表格背景 */
            var $gantt_content_grid = $('<div class="ganttchart-gantt-content-grid">');
            $gantt_content.append($gantt_content_grid);

            /* 右侧甘特图 内容区 甘特图 数据块 */
            var $gantt_content_blocks = $('<div class="ganttchart-gantt-content-blocks">');
            $gantt_content.append($gantt_content_blocks);
            
            opts._build_toolbar();
        };

        opts._build_toolbar = function(){
            var $toolbar = $(".ganttchart-toolbar", opts.dom);
            var $toolbar_right = $("<div class='ganttchart-toolbar-right'></div>");
            $toolbar.append($toolbar_right);

            /* 显示粒度 功能区 */
            $toolbar_right.append("显示粒度：");
            $toolbar_right.append("<button class='ganttchart-toolbar-btn quarter'>季</button>");
            $toolbar_right.append("<button class='ganttchart-toolbar-btn month'>月</button>");
            $toolbar_right.append("<button class='ganttchart-toolbar-btn week'>周</button>");
            $toolbar_right.append("<button class='ganttchart-toolbar-btn day'>天</button>");
            
            $(".ganttchart-toolbar-btn.quarter").click(function(){
                opts.mode = "quarter";
                opts._build_gantt();
            });
            $(".ganttchart-toolbar-btn.month").click(function(){
                opts.mode = "month";
                opts._build_gantt();
            });
            $(".ganttchart-toolbar-btn.week").click(function(){
                opts.mode = "week";
                opts._build_gantt();
            });
            $(".ganttchart-toolbar-btn.day").click(function(){
                opts.mode = "day";
                opts._build_gantt();
            });


        };

        /* 加载 数据标题 部分 */
        opts._build_head_title = function(){
            var $head_title = $(".ganttchart-head-title", opts.dom);
            $head_title.empty();

            if(typeof opts.columns == "undefined" || opts.columns == null || opts.columns.length <= 0){ return; }
            
            var width_count = 0;
            for(var i = 0; i < opts.columns.length; i++){
                var col = opts.columns[i];
                
                if(typeof col.hidden != "undefined" && col.hidden == true){  /* 该列需要隐藏，不再处理 */ continue; }

                width_count += col.width;
                $head_title.append(
                    $('<div class="ganttchart-head-title-item"></div>')
                    .css("width", (col.width || opts.default_column_width) +"px")
                    .append(col.name)
                );
            }
            $head_title.width(width_count);
            $(".ganttchart-head-content", opts.dom).width(width_count);
        };

        /* 加载 数据内容 部分 */
        opts._build_head_content = function(){
            var $head_content = $(".ganttchart-head-content", opts.dom);
            $head_content.empty();

            if(typeof opts.columns == "undefined" || opts.columns == null || opts.columns.length <= 0){ return; }
            if(typeof opts.data == "undefined" || opts.data == null || opts.data.length <= 0){ return; }
            
            for(var i = 0; i < opts.data.length; i++){ /* 遍历数据列表 */
                var obj = opts.data[i];
                var $row = $("<div class='ganttchart-head-content-row'></div>")  /* 创建一个数据行 */
                if(i % 2 == 1) $row.addClass("even");

                if(obj.Color && obj.Color != "" && obj.Color.length > 0){
                    $row.css("background-color", obj.Color);
                }

                for(var j = 0; j < opts.columns.length; j++){  /* 遍历 数据列 集合，依次读取各列属性值 */
                    var col = opts.columns[j];
                    var colContent = obj[col.data];

                    if(typeof col.hidden != "undefined" && col.hidden == true){  /* 该列需要隐藏，不再处理 */ continue; }

                    if(col.data == "StartTime" || col.data == "EndTime"){
                        colContent = opts._format_date(colContent);
                    }else if(col.data == "Index"){
                        colContent = i + 1;
                    }else{
                        if(col.formatter && typeof col.formatter == "function"){
                            colContent = col.formatter(i+1, colContent, obj);
                        }
                    }

                    $row.append(
                        $('<div class="ganttchart-head-content-item"></div>')
                        .css("width", (col.width || opts.default_column_width) +"px")
                        .append(colContent)
                    );
                }
                $head_content.append($row);
            }
        };

        /* 构造 甘特图 区域 */
        opts._build_gantt = function(){
            $(".ganttchart-toolbar-btn").removeClass("focus");
            $(".ganttchart-toolbar-btn." + opts.mode).addClass("focus");

            opts._read_gantt_groups();  /* 读取 甘特图 分组结构 */
            opts._build_gantt_title();  /* 构造 甘特图 标题 */
            opts._build_gantt_content();  /* 构造 甘特图 内容  */
        };

        /* 加载 甘特图标题 部分  */
        opts._build_gantt_title = function(){
            var $gantt_title = $(".ganttchart-gantt-title", opts.dom);
            var $gantt_title_groups = $(".ganttchart-gantt-title-groups", opts.dom);
            $gantt_title_groups.empty();

            var $gantt_title_items = $(".ganttchart-gantt-title-items", opts.dom);
            $gantt_title_items.empty();

            var total_width = 0;

            for(var index in opts.GanttGroup.Groups){
                var group = opts.GanttGroup.Groups[index];
                var $dom_group = $('<div class="ganttchart-gantt-title-group"></div>').css("width", group.Width + "px").append(group.Name).attr("title", group.Name);
                $gantt_title_groups.append($dom_group);

            }
            for(var index in opts.GanttGroup.Items){
                var item = opts.GanttGroup.Items[index];
                var $dom_item = $('<div class="ganttchart-gantt-title-item"></div>').css("width", opts.GanttCellWidth + "px").append(item.Name).attr("title", item.Name);
                $gantt_title_items.append($dom_item);
                total_width += opts.GanttCellWidth;
            }
            $gantt_title_groups.width(total_width);
            $gantt_title_items.width(total_width);
            $gantt_title.width(total_width);
        };

        /* 加载 甘特图内容 部分 */
        opts._build_gantt_content = function(){
            var $gantt_content = $(".ganttchart-gantt-content", opts.dom);
            var $gantt_content_grid = $(".ganttchart-gantt-content-grid", opts.dom);
            var $gantt_content_blocks =  $(".ganttchart-gantt-content-blocks", opts.dom);
            $gantt_content_grid.empty();
            $gantt_content_blocks.empty();


            for(var i = 0; i < opts.data.length; i++){ /* 遍历数据列表 */
                var obj = opts.data[i];

                /* 绘制表格行 */
                var $dom_grid_row = $('<div class="ganttchart-gantt-content-grid-row"></div>')
                                    .css("width", $(".ganttchart-gantt-title", opts.dom).width())
                                    .addClass("gantt-grid-row-" + opts.GanttCellWidth);
                if(i % 2 == 1) $dom_grid_row.addClass("even");
                $gantt_content_grid.append($dom_grid_row);
                
                /* 绘制表格 单元格。。绘制时，单元格太多会卡死。。放弃该方案。 */
                /*
                for(var j = 0; j < opts.GanttGroup.Items.length; j++){
                    var $dom_item = $('<div class="ganttchart-gantt-content-grid-row-cell"></div>').css("width", opts.GanttCellWidth + "px");
                    $dom_grid_row.append($dom_item);
                }
                */
                
                var $dom_block_row = $('<div class="ganttchart-gantt-content-block-gantt"></div>');
                $gantt_content_blocks.append($dom_block_row);

                var block_span = opts._get_time_between_span(obj.StartTime, obj.EndTime);
                var block_margin = opts._get_time_between_span(opts.start, obj.StartTime) - 1;

                var $dom_block = $('<div class="ganttchart-gantt-content-block"></div>')
                                .attr("title", obj.Name)
                                .css("width", block_span * opts.GanttCellWidth)
                                .css("margin-left", block_margin * opts.GanttCellWidth);
                if(obj.Color && obj.Color != "" && obj.Color.length > 0){
                    $dom_block.css("background-color", obj.Color);
                }

                $dom_block_row.append($dom_block);
            }

        };

        /* 从甘特图数据里读取时间区域 */
        opts._read_time_area = function(){
            if(typeof opts.data == "undefined" || opts.data == null || opts.data.length <= 0){ return; }
            
            for(var i = 0; i < opts.data.length; i++){ /* 遍历数据列表 */
                var obj = opts.data[i];
                if(i == 0){
                    opts.start = obj.StartTime;
                    opts.end = obj.EndTime;
                }else{
                    if(opts.start > obj.StartTime) opts.start = obj.StartTime;
                    if(opts.end < obj.EndTime) opts.end = obj.EndTime;
                }                
            }
        };

        /* 读取甘特图的总体 分组组合 */
        opts._read_gantt_groups = function(){
            opts.GanttGroup = opts._get_between_groups(opts.start, opts.end);
        };

        /* 获取两个日期间的时间跨度 */
        opts._get_time_between_span = function(start, end){
            var group = opts._get_between_groups(start, end);
            var count = group.Items.length;
            
            return count;
        };

        /* 获取两个日期之间的分组组合 */
        opts._get_between_groups = function(start, end){
            var group = { Groups: new Array(), Items: new Array() };
        
            if(opts.mode == "quarter"){
                opts.GanttCellWidth = 80;
                for(var year = start.getFullYear(); year<=end.getFullYear(); year++){
                    var mStart = 0;
                    var mEnd = 0;
                    if(start.getFullYear() == year){ mStart = start.getMonth(); } else{ mStart = 0; }
                    if(end.getFullYear() == year){ mEnd = end.getMonth();  } else{ mEnd = 11; }
                    mStart = parseInt(mStart / 3) + 1;
                    mEnd = parseInt(mEnd / 3) + 1;

                    for(var item = mStart; item <= mEnd; item++){
                        group.Items.push({ Name: "Q" + item });
                    }
                    
                    group.Groups.push({ Name: year, Width: (mEnd - mStart + 1) * opts.GanttCellWidth});
                }
            }else if(opts.mode == "month"){
                opts.GanttCellWidth = 50;
                for(var year = start.getFullYear(); year<=end.getFullYear(); year++){
                    var mStart = 0;
                    var mEnd = 0;
                    if(start.getFullYear() == year){ mStart = start.getMonth() + 1; } else{ mStart = 1; }
                    if(end.getFullYear() == year){ mEnd = end.getMonth() + 1;  } else{ mEnd = 12; }

                    for(var month = mStart; month <= mEnd; month++){
                        group.Items.push({ Name: month });
                    }
                    group.Groups.push({ Name: year, Width: (mEnd - mStart + 1) * opts.GanttCellWidth});
                }
            }else if(opts.mode == "week"){
                opts.GanttCellWidth = 28;
                for(var year = start.getFullYear(); year <= end.getFullYear(); year++){
                    var mStart = 0;
                    var mEnd = 0;
                    if(start.getFullYear() == year){ mStart = start.getMonth(); } else{ mStart = 0; }
                    if(end.getFullYear() == year){ mEnd = end.getMonth();  } else{ mEnd = 11; }
                    for(var month = mStart; month <= mEnd; month++){
                        var key = year + "/" + (month + 1);
                        
                        var offset_left = 0;
                        var offset_right = 0;
                        var dStart = 0;
                        var dEnd = 0;
                        if(start.getFullYear() == year && start.getMonth() == month){
                            dStart = start.getDate();
                            offset_left = start.getDay() - 1;
                        } else{ dStart = 1; }
                        if(end.getFullYear() == year && end.getMonth() == month){ 
                            dEnd = end.getDate();
                            offset_right = 7 - end.getDay();
                        } else {
                            var tmpDate = new Date(year, month + 1, 1);
                            tmpDate.setDate(tmpDate.getDate() - 1)
                            dEnd = tmpDate.getDate();
                        }

                        for(var day = dStart - offset_left; day <= dEnd + offset_right; day++){
                            var tmpDate = new Date(year, month, day);
                            if(tmpDate.getDay() == 1){
                                group.Items.push({ Name: day });
                            }
                        }
                        
                        group.Groups.push({ Name: key, Width: (dEnd + offset_right - dStart + offset_left + 1) / 7 * opts.GanttCellWidth});
                    }
                }
            }else if(opts.mode == "day"){
                opts.GanttCellWidth = 20;
                for(var year = start.getFullYear(); year <= end.getFullYear(); year++){
                    var mStart = 0;
                    var mEnd = 0;
                    if(start.getFullYear() == year){ mStart = start.getMonth(); } else{ mStart = 0; }
                    if(end.getFullYear() == year){ mEnd = end.getMonth();  } else{ mEnd = 11; }
                    for(var month = mStart; month <= mEnd; month++){
                        var key = year + "/" + (month + 1);
                        
                        var dStart = 0;
                        var dEnd = 0;
                        if(start.getFullYear() == year && start.getMonth() == month){ dStart = start.getDate(); } else{ dStart = 1; }
                        if(end.getFullYear() == year && end.getMonth() == month){ dEnd = end.getDate();  } else {
                            var tmpDate = new Date(year, month + 1, 1);
                            tmpDate.setDate(tmpDate.getDate() - 1)
                            dEnd = tmpDate.getDate();
                        }
                        for(var day = dStart; day <= dEnd; day++){
                            group.Items.push({ Name: day });
                        }
                        
                        group.Groups.push({ Name: key, Width: (dEnd - dStart + 1) * opts.GanttCellWidth});
                    }
                }
            }
            return group;
        };

        /* 格式化日期 */
        opts._format_date = function(date) {
            if (typeof (date) == "undefined" || date == null) { return ""; }
            
            if (("" + date).length == 19 && date.indexOf("T") == 10) {
                return date.substr(0, 10);
            }
        
            if (!date.getDate) {
                date = new Date(parseInt(date.replace("/Date(", "").replace(")/", ""), 10));
            }
        
            var datestr = "";
            var m = date.getMonth() + 1;
            var y = date.getFullYear();
            var d = date.getDate();
        
            datestr += y;
            datestr += '-';
            datestr += m < 10 ? "0" + m : m;
            datestr += '-';
            datestr += d < 10 ? "0" + d : d;
        
            return datestr;
        };

        /* ------------------------------------------------ */
        return this.each(function () {
            $.data(this, 'ganttchart', { options: opts });
            opts._init();
        });
    };/* ganttchart Function End */





    /* ============================== ==============================
        以下为接口方法，和默认值
       ============================== ==============================*/

    /* 方法集 */
    $.fn.ganttchart.methods = {
        options: function (sender) {
            return $.data(sender[0], 'ganttchart').options;
        }
        /* 获取指定单元格所在的行索引 * /
        getRowIndex: function (sender, cell) {
            var options = $.data(sender[0], 'ganttchart').options;
            return options._get_row_index(cell);
        }
/* 获取指定单元格所在的行索引 */
    };

    /* 默认值 */
    $.fn.ganttchart.defaults = {
        rows: 0
        ,default_column_width: 20
    };

});

